package com.xjrsoft.common.utils;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.xjrsoft.common.constant.GlobalConstant;
import com.xjrsoft.common.mqtt.MqttCallBackModel;
import com.xjrsoft.common.mqtt.MqttProviderCallBack;
import com.xjrsoft.common.mqtt.OpcUaClientService;
import com.xjrsoft.module.iot.entity.IotDic;
import com.xjrsoft.module.iot.entity.IotLink;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @Author: tzx
 * @Date: 2024/5/10 14:26
 */
@Slf4j
public class IotUtil {

    public static Map<Long, OpcUaClient> cacheOpcUaClientMap = new HashMap<>();
    public static Map<Long, MqttClient> cacheMqttClientMap = new HashMap<>();

    private static Boolean isUpdate = false;

    /**
     * 构建mqtt 连接
     *
     * @param iotLink
     */
    public static void initMqttServer(IotLink iotLink) {


        try {
            MqttClient mqttClient = new MqttClient(iotLink.getHostUrl(), iotLink.getClientId() + LocalDateTime.now(), new MemoryPersistence());
            MqttConnectOptions options = new MqttConnectOptions();

            MqttCallBackModel model = new MqttCallBackModel();

            model.setClient(mqttClient);
            model.setIotLink(iotLink);

            mqttClient.setCallback(new MqttProviderCallBack(model));
            if (StrUtil.isNotEmpty(iotLink.getUsername()) && StrUtil.isNotEmpty(iotLink.getPassword())) {
                options.setUserName(iotLink.getUsername());
                options.setPassword(iotLink.getPassword().toCharArray());
            }

            options.setAutomaticReconnect(true);
            options.setCleanSession(true);
            options.setConnectionTimeout(10);

            options.setKeepAliveInterval(20);
//            options.setWill(iotLink.getTopic(), "offline".getBytes(), 0, false);
            mqttClient.setTimeToWait(5000);
            mqttClient.connect(options);

            mqttClient.subscribe(iotLink.getTopic(), 2);
            log.info("mqtt host:{} clientId:{} 已经连接上", iotLink.getHostUrl(), iotLink.getClientId());

            cacheMqttClientMap.put(iotLink.getId(), mqttClient);
        } catch (MqttException e) {
            e.printStackTrace();
        }

    }


    /**
     * 构建opcUa 连接
     *
     * @param iotLink
     */
    public static void initOpcuaServer(IotLink iotLink) {

        try {
            OpcUaClientService opcUaClientService = new OpcUaClientService();

            // 与OPC UA服务端建立连接，并返回客户端实例
            OpcUaClient client = opcUaClientService.connectOpcUaServer(iotLink.getHostUrl(), iotLink.getUsername(), iotLink.getPassword());

            List<IotDic> linkDicList = IotUtil.initLinkDic(iotLink);

            // 订阅指定节点
//            opcUaClientService.subscribe(client, 2, "Dynamic/RandomDouble");

            for (IotDic iotDic : linkDicList) {
                // 订阅指定节点
                opcUaClientService.subscribe(client, 2, iotDic.getName(), linkDicList, true);
            }

            cacheOpcUaClientMap.put(iotLink.getId(), client);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }


    /**
     * 构建当前链接的 数据字典
     *
     * @param iotLink
     * @return
     */
    public static List<IotDic> initLinkDic(IotLink iotLink) {

        RedisUtil redisUtil = SpringUtil.getBean(RedisUtil.class);
        List<IotDic> dicList = redisUtil.get(GlobalConstant.IOT_DIC_CACHE_KEY, new TypeReference<List<IotDic>>() {
        });
        return dicList.stream().filter(dic -> dic.getLinkId().equals(iotLink.getId())).collect(Collectors.toList());
    }


    public static void setUpdate(Boolean update) {
        isUpdate = update;
    }

    public static boolean isNeedUpdate() {
        return isUpdate;
    }


    public static Object transValue(IotDic iotDic, Object value) {
        if (ObjectUtil.isNull(value)) {
            return null;
        }

        switch (iotDic.getTrans()) {
            case 0:
                return value;
            case 1:
                try {
                    if (Convert.toBigDecimal(value).compareTo(Convert.toBigDecimal(iotDic.getTransValue())) > 0) {
                        return value;
                    }
                } catch (Exception e) {
                    log.info("转换出现问题");
                }
                break;
            case 2:
                try {
                    return Math.sqrt(Convert.toDouble(value));
                } catch (Exception e) {
                    log.info("转换出现问题");
                }
                break;
            case 3:
                try {
                    String transValue = iotDic.getTransValue();
                    String[] split = transValue.split(":");
                    //原始值
                    String originValue = split[0];
                    //工程值
                    String targetValue = split[1];

                    String[] originSplit = originValue.split("-");
                    //原始最大值
                    String originMin = originSplit[0];
                    //原始最小值
                    String originMax = originSplit[1];

                    String[] targetSplit = targetValue.split("-");
                    //原始最大值
                    String targetMin = targetSplit[0];
                    //原始最小值
                    String targetMax = targetSplit[1];

                    //线性转换 获取线性比例
                    double v = (Convert.toDouble(originMax) - Convert.toDouble(originMin)) / (Convert.toDouble(targetMax) - Convert.toDouble(targetMin));

                    return Convert.toDouble(value) * v;
                } catch (Exception e) {
                    log.info("转换出现问题");
                }
                break;
            case 4:
                try {
                    return Convert.toDouble(value) + Convert.toDouble(iotDic.getTransValue());
                } catch (Exception e) {
                    log.info("转换出现问题");
                }
                break;
        }
        return null;
    }
}
